package com.demo.cms.security.access.audit.advice;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Date;

import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.security.core.Authentication;
import org.springframework.util.ReflectionUtils;

import com.demo.common.AopProxyUtils;
import com.demo.core.model.Audit;
import com.demo.core.model.Site;
import com.demo.core.service.base.AuditService;
import com.demo.security.access.audit.AuditModel;
import com.demo.security.access.audit.prepost.PostAuditAttribute;
import com.demo.security.access.audit.prepost.PreAuditAttribute;
import com.demo.security.access.audit.prepost.PrePostInvocationAuditAdvice;
import com.demo.security.annotation.audit.AuditNotes;
import com.demo.security.util.SecurityUtil;
import com.demo.webapp.context.MultisiteContextHolder;

/**
 * CMS站点的前置后置审计处理类
 * 指定业务服务被调用时,该审计方法将自动被调用。
 * 使用AuditService服务进行审计。
 * @author ryuu.kk
 *
 */
public class CmsPrePostInvocationAuditAdviceImpl implements PrePostInvocationAuditAdvice {

	/**日志对象**/
    private static final Log LOG = LogFactory.getLog(CmsPrePostInvocationAuditAdviceImpl.class);
    
	/**
	 * 审计级别,该对象只对指定审计级别及以上级别进行审计
	 * 0:debug级别
	 * 1-5: 审计业务级别
	 */
	private int level = 0;
	
	/**
	 * 审计字段Format
	 */
	private String format;
	
	/**
	 * 审计服务
	 */
	private AuditService auditService;
	
	@Override
	public void before(Authentication authentication, MethodInvocation mi, PreAuditAttribute preAuditAttribute) {
		preAuditAttribute.getAuditModel();
		mi.getArguments();
		System.err.println("Pre-Audit" + authentication.getPrincipal() + preAuditAttribute.getAttribute());
		if (LOG.isDebugEnabled()) {
			LOG.debug("Pre-Audit" + authentication.getPrincipal() + preAuditAttribute.getAttribute());
		}
	}

	@Override
	public Object after(Authentication authentication, MethodInvocation mi,	PostAuditAttribute postAuditAttribute, Object returnObject) {
		AuditModel model = postAuditAttribute.getAuditModel();
		
		// 当前方法审计级别小于给定需要审计级别时,不做审计处理
		if (model.getLevel() < level) {
			return returnObject;
		}
		
		Object[] args = mi.getArguments();
		Method method = mi.getMethod();
		Object auditNotes = null;
		
		//ReflectionUtils.findField(clazz, name)
		Class<?> targetClass = AopUtils.getTargetClass(mi.getThis());
		Field[] fields = targetClass.getDeclaredFields();

		for (Field field : fields) {
			// 判断字段是否使用了AuditNotes注解
			AuditNotes notes = field.getAnnotation(com.demo.security.annotation.audit.AuditNotes.class);
			if (notes != null) {
				String type = notes.type();
				System.out.println(" audit target type " + type);
				try {
					// 允许访问私有变量
					field.setAccessible(true);
					auditNotes = ReflectionUtils.getField(field, AopProxyUtils.ultimateTargetObject(mi.getThis()));
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (Throwable e) {
					e.printStackTrace();
				}
				break;
			}
		}
		
		Audit audit = auditBuilder(authentication, model, auditNotes, targetClass, method, args, returnObject);
		try {
			auditService.save(audit);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e.getCause());
		}
		System.err.println("Post-Audit " + authentication.getPrincipal() + postAuditAttribute.getAttribute() + "\n Audit :" + auditNotes);
		return returnObject;
	}
	
	public Audit auditBuilder(Authentication authentication, AuditModel model, Object auditNotes, Class<?> targetClass, Method method, Object[] args, Object result) {
		
		Long userId = SecurityUtil.getCurrentUserId();
		Site site = MultisiteContextHolder.getSite();
		Long siteId = site == null ? null : site.getId();
		Audit audit = new Audit();
		audit.setUserName(authentication.getName());
		String action = targetClass.getName() + "." +method.getName();
		String input = " no-arguments;";
		String putput = "";
		// input
		if (args != null) {
			input = "";
			for (Object obj : args) {
				input += obj == null ? "" : obj.getClass().getName() + ":" + obj.toString() + "; "; 
			}
		}
		// output
		putput = result == null ? "no-result;" : result.getClass().getName() + ":" + result.toString();
		
		audit.setSiteId(siteId);
		audit.setAction(action);
		audit.setActionType(model.getCode());
		audit.setInput(input);
		audit.setOutput(putput);
		audit.setCreateDate(new Date());
		audit.setCreateUser(userId);
		audit.setNotes(auditNotes == null ? "no-notes" : auditNotes.toString());
		
		return audit;
		
	}
	
	public void setLevel(int level) {
		this.level = level;
	}
	
	public void setFormat(String format) {
		this.format = format;
	}

	// 审计服务注入
	public void setAuditService(AuditService auditService) {
		this.auditService = auditService;
	}
	
}
